// RootFolder.cpp

#include "StdAfx.h"

#include "Common/System/DLL.h"
#include "Common/MS/PropVariant.h"

#include "Common/7Zip/PropID.h"

#include "FSFolder.h"
#include "NetFolder.h"
#include "FSDrives.h"
#include "RootFolder.h"

#include "FileName.h"

#include "SysIconUtils.h"

#include <atlbase.h>

using namespace Common;

namespace Common
{
namespace FileSystem
{

static const STATPROPSTG kProps[] =
{
	{ NULL, kpidName, VT_BSTR}
};

enum
{
	ROOT_INDEX_COMPUTER = 0
						  , ROOT_INDEX_DOCUMENTS
	, ROOT_INDEX_NETWORK
	, ROOT_INDEX_VOLUMES
};

static LPCTSTR kVolPrefix = _T("\\\\.");

HRESULT CRootFolder::Init()
{
	_names[ROOT_INDEX_COMPUTER] = RootFolder_GetName_Computer(_iconIndices[ROOT_INDEX_COMPUTER]);
	_names[ROOT_INDEX_DOCUMENTS] = RootFolder_GetName_Documents(_iconIndices[ROOT_INDEX_DOCUMENTS]);
	_names[ROOT_INDEX_NETWORK] = RootFolder_GetName_Network(_iconIndices[ROOT_INDEX_NETWORK]);
	_names[ROOT_INDEX_VOLUMES] = kVolPrefix;
	_iconIndices[ROOT_INDEX_VOLUMES] = FileSystem::GetIconIndexForCSIDL(CSIDL_DRIVES);
	//return LoadItems();
	return S_OK;
}

STDMETHODIMP CRootFolder::LoadItems()
{
	Init();
	return S_OK;
}

STDMETHODIMP CRootFolder::GetNumberOfItems(UINT32* numItems)
{
	*numItems = kNumRootFolderItems;
	return S_OK;
}

STDMETHODIMP CRootFolder::GetProperty(UINT32 itemIndex, PROPID propID, PROPVARIANT* value)
{
	CComPropVariant prop;
	switch(propID)
	{
		case kpidIsDir:
			prop = true;
			break;
		case kpidName:
			prop = CComBSTR(_names[itemIndex]);
			break;
	}
	prop.Detach(value);
	return S_OK;
}

CString GetMyDocsPath()
{
	CString us;
	::SHGetSpecialFolderPath(0, us.GetBufferSetLength(MAX_PATH), CSIDL_PERSONAL, FALSE);

	if (us.GetLength() > 0 && us[us.GetLength() - 1] != CHAR_PATH_SEPARATOR)
	{
		us += CHAR_PATH_SEPARATOR;
	}
	return us;
}

STDMETHODIMP CRootFolder::BindToFolder(UINT32 index, IFolderFolder** resultFolder)
{
	*resultFolder = NULL;
	CComPtr<IFolderFolder> subFolder;
	if (index == ROOT_INDEX_COMPUTER || index == ROOT_INDEX_VOLUMES)
	{
		ATLTRACE2(atlTraceCOM, 0, _T("BindToFolder: bind to root folder\n"));
		CFSDrives* fsDrivesSpec = new CComObject<CFSDrives>;
		fsDrivesSpec->FinalConstruct();

		subFolder = fsDrivesSpec;
		fsDrivesSpec->Init(index == ROOT_INDEX_VOLUMES);
	}
	else if (index == ROOT_INDEX_NETWORK)
	{
		ATLTRACE2(atlTraceCOM, 0, _T("BindToFolder: bind to network folder: %s\n"), _names[ROOT_INDEX_NETWORK]);
		Net::CNetFolder* netFolderSpec = new CComObject<Net::CNetFolder>;
		netFolderSpec->FinalConstruct();

		subFolder = netFolderSpec;
		netFolderSpec->Init(0, 0, _names[ROOT_INDEX_NETWORK] + CHAR_PATH_SEPARATOR);
	}
	else if (index == ROOT_INDEX_DOCUMENTS)
	{
		CString s = GetMyDocsPath();
		if (!s.IsEmpty())
		{
			ATLTRACE2(atlTraceCOM, 0, _T("BindToFolder: bind to documents folder: %s\n"), s);
			FileSystem::Folder::CFSFolder* fsFolderSpec = new CComObject<FileSystem::Folder::CFSFolder>;
			fsFolderSpec->FinalConstruct();
			subFolder = fsFolderSpec;
			HRESULT hRes(fsFolderSpec->Init(s, NULL));
			if(S_OK != hRes)
			{
				return hRes;
			}
		}
	}
	else
	{
		return E_INVALIDARG;
	}
	*resultFolder = subFolder.Detach();
	return S_OK;
}

static bool AreEqualNames(const CString &name1, const CString &name2)
{
	return (name1 == name2 || name1 == (name2 + CString(CHAR_PATH_SEPARATOR)));
}

STDMETHODIMP CRootFolder::BindToFolder(const wchar_t* name, IFolderFolder** resultFolder)
{
	*resultFolder = 0;
	CString name2 = name;
	name2.Trim();
	if (name2.IsEmpty())
	{
		CRootFolder* rootFolderSpec = new CComObject<CRootFolder>;
		rootFolderSpec->FinalConstruct();
		CComPtr<IFolderFolder> rootFolder = rootFolderSpec;
		rootFolderSpec->Init();
		*resultFolder = rootFolder.Detach();
		return S_OK;
	}
	for (int i = 0; i < kNumRootFolderItems; i++)
		if (AreEqualNames(name2, _names[i]))
		{
			return BindToFolder((UINT32)i, resultFolder);
		}
	if (AreEqualNames(name2, _T("My Documents")) ||
			AreEqualNames(name2, _T("Documents")))
	{
		return BindToFolder((UINT32)ROOT_INDEX_DOCUMENTS, resultFolder);
	}
	if (AreEqualNames(name2, _T("My Computer")) ||
			AreEqualNames(name2, _T("Computer")))
	{
		return BindToFolder((UINT32)ROOT_INDEX_COMPUTER, resultFolder);
	}
	if (name2 == CString(CHAR_PATH_SEPARATOR))
	{
		CComPtr<IFolderFolder> subFolder = this;
		*resultFolder = subFolder.Detach();
		return S_OK;
	}

	if (name2.GetLength () < 2)
	{
		return E_INVALIDARG;
	}

	CComPtr<IFolderFolder> subFolder;

	if (name2.Left(4) == kVolPrefix)
	{
		CFSDrives* folderSpec = new CComObject<CFSDrives>;
		folderSpec->FinalConstruct();
		subFolder = folderSpec;
		folderSpec->Init(true);
	}
	else
	{
		if (name2[name2.GetLength () - 1] != CHAR_PATH_SEPARATOR)
		{
			name2 += CHAR_PATH_SEPARATOR;
		}
		FileSystem::Folder::CFSFolder* fsFolderSpec = new CComObject<FileSystem::Folder::CFSFolder>;
		fsFolderSpec->FinalConstruct();
		subFolder = fsFolderSpec;
		if (fsFolderSpec->Init(name2, 0) != S_OK)
		{
#ifndef UNDER_CE
			if (name2[0] == CHAR_PATH_SEPARATOR)
			{
				Net::CNetFolder* netFolderSpec = new CComObject<Net::CNetFolder>;
				netFolderSpec->FinalConstruct();
				subFolder = netFolderSpec;
				netFolderSpec->Init(name2);
			}
			else
#endif
				return E_INVALIDARG;
		}
	}
	*resultFolder = subFolder.Detach();
	return S_OK;
}

STDMETHODIMP CRootFolder::BindToParentFolder(IFolderFolder** resultFolder)
{
	*resultFolder = 0;
	return S_OK;
}

STDMETHODIMP CRootFolder::GetNumberOfProperties(UINT32* numProperties)
{
	*numProperties = sizeof(kProps) / sizeof(kProps[0]);
	return S_OK;
}
STDMETHODIMP CRootFolder::GetPropertyInfo(UINT32 index, BSTR* name, PROPID* propID, VARTYPE* varType)
{
	if(index >= sizeof(kProps) / sizeof(kProps[0]))
	{
		return E_INVALIDARG;
	}
	const STATPROPSTG &srcItem = kProps[index];
	*propID = srcItem.propid;
	*varType = srcItem.vt;
	*name = 0;
	return S_OK;
}


STDMETHODIMP CRootFolder::GetFolderProperty(PROPID propID, PROPVARIANT* value)
{
	CComPropVariant prop;
	switch(propID)
	{
		case kpidType:
			prop = CComBSTR(_T("RootFolder"));
			break;
		case kpidPath:
			prop = CComBSTR(_T(""));
			break;
	}
	prop.Detach(value);
	return S_OK;
}

STDMETHODIMP CRootFolder::GetSystemIconIndex(UINT32 index, INT32* iconIndex)
{
	*iconIndex = _iconIndices[index];
	return S_OK;
}

}
}